﻿
using System;
using System.Threading;

namespace WSU.ChekaParallel.TestThread
{
    static class TestThread
    {
        private sealed class Fool
        {
            private readonly string m_msg;
            private readonly int m_numTimes;
            private readonly TimeSpan m_interval;

            public Fool(string msg, int numTimes, TimeSpan interval)
            {
                m_msg = msg;
                m_numTimes = numTimes;
                m_interval = interval;
            }

            public void Run()
            {
                for (int index = 0; index < m_numTimes; index++)
                {
                    Console.WriteLine("{0} {1}", index + 1, m_msg);
                    Thread.Sleep(m_interval);
                }
            }
        }

        /// <summary>
        /// test launch threads without arguments
        /// </summary>
        private static void TestStartThreadWithoutArg()
        {
            const int MAX = 100;

            Thread thrdY = new Thread(() =>
            {
                for (int index = 0; index < MAX; ++index)
                    Console.Write("Y");
            });
            thrdY.Start();

            for (int index = 0; index < MAX; ++index)
                Console.Write("x");
        }

        private static void TestJoin()
        {
            const int MAX = 100;

            Thread thrdY = new Thread(() =>
            {
                for (int index = 0; index < MAX; ++index)
                    Console.Write("Y");
            });
            thrdY.Start();
            thrdY.Join();

            for (int index = 0; index < MAX; ++index)
                Console.Write("x");
        }

        private static void PrintMsg(object obj)
        {
            Tuple<string, int> parms = (Tuple<string, int>)obj;

            for (int index = 0; index < parms.Item2; ++index)
                Console.Write(parms.Item1);
        }

        private static void TestStartThreadWithArg()
        {
            Thread thrdY = new Thread(PrintMsg);
            thrdY.Start(Tuple.Create("y", 100));

            PrintMsg(Tuple.Create("X", 100));
        }

        private static void TestLambdaCapturedVar()
        {
            // -------------------- capture the same variable
            // cause race condition between main thread's loop and child thread's "Console.Writeline"
            Console.WriteLine("******** capture the same variable ********");
            for (int index = 0; index < 10; ++index)
                new Thread(() =>
                {
                    Console.WriteLine(index.ToString());
                    Thread.Sleep(20);
                }).Start();

            Console.WriteLine("SLEEP TO WAIT, ... ...");
            Thread.Sleep(1000);

            // -------------------- each time capture its own variable
            Console.WriteLine("******** capture its own copy ********");
            for (int index = 0; index < 10; ++index)
            {
                int ownCopy = index;// let each lambda expression capture its own copy
                new Thread(() =>
                {
                    Console.WriteLine(ownCopy.ToString());
                    Thread.Sleep(10);
                }).Start();
            }
        }

        /// <summary>
        /// note: without a command line argument, the newly created thread is foreground thread
        /// the process will not exit until all foreground threads completes
        /// so after printing "Main thread exits !!!", the whole process still NOT terminates
        /// until you press ENTER which ends the thread
        /// note: if passing a command line argument, then the thread will be background thread
        /// and the process will not wait for background to complete before exitting
        /// after printing "Main thread exits !!!", the whole process terminates
        /// even though the thread still blocks and is alive at that time
        /// </summary>
        /// <param name="args"></param>
        private static void TestForeOrBackground(string[] args)
        {
            Thread thrd = new Thread(() => Console.ReadLine());
            Console.WriteLine("explicit created thread are always foreground? {0}", (!thrd.IsBackground).ToString());

            if (args.Length > 0)
            {
                thrd.IsBackground = true;
                Console.WriteLine("change thread to background");
            }

            thrd.Start();
            Console.WriteLine("Main thread exits !!!");
        }

        private static void TestRunInstanceMethod()
        {
            var f1 = new Fool("hello c#", 10, TimeSpan.FromMilliseconds(100));
            new Thread(f1.Run).Start();

            var f2 = new Fool("!!!!! go cougar !!!!!", 5, TimeSpan.FromMilliseconds(250));
            new Thread(f2.Run).Start();

            // note: we don't need below codes to prevent the program to exit
            // because by default, all thread by "new Thread().Start()" are foreground thread
            // the whole program will not exit until all foreground thread completes
            //Console.WriteLine("Press <ENTER> to continue, ......");
            //Console.ReadLine();
            Console.WriteLine("main thread completes here (but other foreground threads prevent the whole program exits)");
        }

        // ===================================================== //
        #region [ entry ]

        public static void TestMain(string[] args)
        {
            // TestStartThreadWithoutArg();
            // TestJoin();
            // TestStartThreadWithArg();
            // TestLambdaCapturedVar();
            TestForeOrBackground(args);
            // TestRunInstanceMethod();
        }

        #endregion
    }
}